/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 artifact_effect.cpp

	$Header: /heroes4/artifact_effect.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "artifact_effect.h"

#include <assert.h>
#include "artifact_effect_visitor.h"
#include "artifact_prop_ability.h"
#include "artifact_prop_aligned_bonus.h"
#include "artifact_prop_combat.h"
#include "artifact_prop_damage.h"
#include "artifact_prop_fragment.h"
#include "artifact_prop_growth.h"
#include "artifact_prop_income.h"
#include "artifact_prop_movement.h"
#include "artifact_prop_single_spell.h"
#include "artifact_prop_skill.h"
#include "artifact_prop_spell_attack.h"
#include "artifact_prop_spell_charges.h"
#include "artifact_prop_spell_cost.h"
#include "elements_of.h"
#include "spell_properties.h"
#include "streambuf_operators.h"

using namespace t_artifact_prop;

namespace
{
	typedef t_artifact_effect* t_create_func();

	t_create_func* k_create_funcs[] =
	{
		t_adventure_movement::create, 
		t_aligned_bonus::create, 
		t_area::create,
		t_block::create, 
		t_cast_spell::create, 
		t_combat_movement::create, 
		t_creature_discount::create, 
		t_creature_growth::create, 
		t_damage::create, 
		t_damage_bonus::create, 
		t_defense::create, 
		t_demonslayer::create, 
		t_extra_attack::create, 
		t_giantslayer::create, 
		t_fragment::create, 
		t_guardian::create, 
		t_health::create, 
		t_immunity::create, 
		t_income::create, 
		t_ignore_enemies::create, 
		t_learn_spell::create, 
		t_long_weapon::create, 
		t_luck::create, 
		t_resistance::create, 
		t_mana::create, 
		t_mana_maximum::create, 
		t_mana_recharge::create, 
		t_morale::create, 
		t_negate_1st_strike::create, 
		t_negate_immunity::create, 
		t_negate_mind_immunity::create, 
		t_negate_protection::create, 
		t_no_melee_penalty::create, 
		t_no_range_penalty::create, 
		t_no_wall_penalty::create, 
		t_panic::create, 
		t_prohibits_retreat::create, 
		t_ranged_backstab::create, 
		t_scouting::create, 
		t_sea_bonus::create, 
		t_seamanship::create, 
		t_soul_stealer::create, 
		t_speed::create, 
		t_spell_effect::create, 
		t_spell_book::create, 
		t_spell_bonus::create, 
		t_spell_charges::create, 
		t_spell_cost::create, 
		t_spell_protection::create, 
		t_spell_with_attack::create, 
		t_skill_percentage::create, 
		t_skill_bonus::create, 
		t_stunning_immunity::create, 
		t_tactics::create, 
		t_terrain_mastery::create,
		t_weapon::create,
		t_give_ability::create,
		t_grail_building::create,
	};

	t_create_func* k_create_table[k_artifact_effect_count];

}; // unnamed namespace

t_artifact_effect_ptr create_effect( t_artifact_effect_type type )
{
	static bool initialized = false;

	if (!initialized)
	{
		initialized = true;

		int i; 

		memset( k_create_table, 0, sizeof( k_create_table ));
		for (i = 0; i < ELEMENTS_OF( k_create_funcs ); i++)
		{
			t_artifact_effect_ptr ptr = k_create_funcs[i]();

			k_create_table[ptr->get_effect()] = k_create_funcs[i];
		}

#if !defined( NDEBUG )
		for (i = 0; i < ELEMENTS_OF( k_create_table ); i++)
			assert( k_create_table[i] != 0 );
#endif
	}

	return k_create_table[type]();
}

// ----------------------------------------------------------------------
// class for artifact effects with no parameters
// ----------------------------------------------------------------------
t_artifact_effect::~t_artifact_effect()
{
}

// ----------------------------------------------------------------------
// class for artifact effects with no parameters
// ----------------------------------------------------------------------
bool t_artifact_effect::read( std::streambuf& buffer )
{
	m_target = t_artifact_target( get<t_uint8>( buffer ));
	// throw away the radius
	get<t_uint8>( buffer );
	m_amount = get<t_int32>( buffer );
	return true;
}

// ----------------------------------------------------------------------
// class for artifact effects with no parameters
// ----------------------------------------------------------------------
bool t_artifact_effect::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit( *this );
}

// ----------------------------------------------------------------------
// class for artifact effects with no parameters
// ----------------------------------------------------------------------
bool t_artifact_effect::write( std::streambuf& buffer ) const
{
	put<t_uint8>( buffer, m_target );
	put<t_uint8>( buffer, 0 );
	put<t_int32>( buffer, m_amount );
	return true;
}

// ----------------------------------------------------------------------
// class for artifact effects with no parameters
// ----------------------------------------------------------------------
int t_artifact_effect::get_level() const
{
	return 0;
}

// ----------------------------------------------------------------------
// class for aligned bonus
// ----------------------------------------------------------------------
bool t_aligned_bonus_base::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	m_alignment    = t_town_type( get<t_uint8>(buffer) );
	m_health_bonus = get<t_int16>( buffer );
	m_speed_bonus  = get<t_int8>( buffer );
	return true;
}


// ----------------------------------------------------------------------
// class for aligned bonus
// ----------------------------------------------------------------------
bool t_aligned_bonus_base::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_aligned_bonus( *this );
}

// ----------------------------------------------------------------------
// class for aligned bonus
// ----------------------------------------------------------------------
bool t_aligned_bonus_base::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	put<t_uint8>(buffer, m_alignment);
	put<t_int16>( buffer, m_health_bonus );
	put<t_int8>( buffer, m_speed_bonus );
	return true;
}

// ----------------------------------------------------------------------
// class for adventure movement bonus
// ----------------------------------------------------------------------
bool t_movement::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	m_affects_land = get<bool>(buffer);
	m_affects_sea = get<bool>(buffer);
	return true;
}

// ----------------------------------------------------------------------
// class for adventure movement bonus
// ----------------------------------------------------------------------
bool t_movement::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_movement( *this );
}

// ----------------------------------------------------------------------
// class for adventure movement bonus
// ----------------------------------------------------------------------
bool t_movement::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	put( buffer, m_affects_land );
	put( buffer, m_affects_sea );
	return true;
}

// ----------------------------------------------------------------------
// class for combat effects
// ----------------------------------------------------------------------
bool t_attack::read( std::streambuf& buffer )
{
	m_affects_melee = get<bool>( buffer );
	m_affects_ranged = get<bool>(buffer );
	return true;
}

// ----------------------------------------------------------------------
// class for combat effects
// ----------------------------------------------------------------------
bool t_attack::write( std::streambuf& buffer ) const
{
	put<bool>( buffer, m_affects_melee );
	put<bool>( buffer, m_affects_ranged );
	return true;
}

// ----------------------------------------------------------------------
// class for combat effects
// ----------------------------------------------------------------------
bool t_combat::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	return t_attack::read( buffer );
}

bool t_combat::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_combat( *this );
}

// ----------------------------------------------------------------------
// class for combat effects
// ----------------------------------------------------------------------
bool t_combat::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	return t_attack::write( buffer );
}

// ----------------------------------------------------------------------
// class for creature growth effect
// ----------------------------------------------------------------------
t_artifact_effect* t_creature_growth::create()
{
	return new t_creature_growth;
}

// ----------------------------------------------------------------------
// class for creature growth effect
// ----------------------------------------------------------------------
t_artifact_effect* t_creature_growth::clone() const
{
	return new t_creature_growth( *this );
}

// ----------------------------------------------------------------------
// class for creature growth effect
// ----------------------------------------------------------------------
bool t_creature_growth::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	::read( buffer, m_bonus, sizeof( m_bonus ));
	return true;
}

// ----------------------------------------------------------------------
// class for creature growth effect
// ----------------------------------------------------------------------
bool t_creature_growth::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_growth( *this );
}

// ----------------------------------------------------------------------
// class for creature growth effect
// ----------------------------------------------------------------------
bool t_creature_growth::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	::write( buffer, m_bonus, sizeof( m_bonus ));
	return true;
};

// ----------------------------------------------------------------------
// class for pieces of an artifact
// ----------------------------------------------------------------------
t_artifact_effect* t_fragment::clone() const
{
	return new t_fragment( *this );
}

// ----------------------------------------------------------------------
// class for pieces of an artifact
// ----------------------------------------------------------------------
t_artifact_effect* t_fragment::create()
{
	return new t_fragment;
}

// ----------------------------------------------------------------------
// class for pieces of an artifact
// ----------------------------------------------------------------------
bool t_fragment::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	m_whole = t_artifact_type( get<t_uint16>( buffer ));
	m_pieces = get<t_uint8>( buffer );
	return true;
}


// ----------------------------------------------------------------------
// class for pieces of an artifact
// ----------------------------------------------------------------------
bool t_fragment::accept( t_artifact_effect_visitor& visitor )
{
	return false;
}

// ----------------------------------------------------------------------
// class for pieces of an artifact
// ----------------------------------------------------------------------
bool t_fragment::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	put<t_uint16>( buffer, m_whole );
	put<t_uint8>( buffer, m_pieces );
	return true;
}

// ----------------------------------------------------------------------
// class for damage bonus
// ----------------------------------------------------------------------
bool t_damage_bonus_base::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	if (!t_attack::read( buffer ))
		return false;
	m_additional_bonus = get<t_int16>( buffer );
	m_levels_per_bonus = get<t_uint8>( buffer );
	m_damage_type = t_damage_type( get<t_uint8>( buffer ));
	return true;
}

// ----------------------------------------------------------------------
// class for damage bonus
// ----------------------------------------------------------------------
bool t_damage_bonus_base::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_damage( *this );
}

// ----------------------------------------------------------------------
// class for damage bonus
// ----------------------------------------------------------------------
bool t_damage_bonus_base::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	if (!t_attack::write( buffer ))
		return false;
	put<t_int16>( buffer, m_additional_bonus );
	put<t_uint8>( buffer, m_levels_per_bonus );
	put<t_uint8>( buffer, m_damage_type );
	return true;
}

// ----------------------------------------------------------------------
// class for income bonus
// ----------------------------------------------------------------------
t_artifact_effect* t_income::clone() const
{
	return new t_income(*this);
}

// ----------------------------------------------------------------------
// class for income bonus
// ----------------------------------------------------------------------
t_artifact_effect* t_income::create()
{
	return new t_income;
}

// ----------------------------------------------------------------------
// class for income bonus
// ----------------------------------------------------------------------
bool t_income::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	::read( buffer, &m_income, sizeof(m_income));
	return true;
}

// ----------------------------------------------------------------------
// class for income bonus
// ----------------------------------------------------------------------
bool t_income::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_income( *this );
}
	
// ----------------------------------------------------------------------
// class for income bonus
// ----------------------------------------------------------------------
bool t_income::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	::write( buffer, &m_income, sizeof(m_income));
	return true;
}

// ----------------------------------------------------------------------
// class for artifacts with a single spell effect
// ----------------------------------------------------------------------
bool t_single_spell::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	m_spell = t_spell( get<t_uint16>( buffer ));
	return true;
}

// ----------------------------------------------------------------------
// class for artifacts with a single spell effect
// ----------------------------------------------------------------------
bool t_single_spell::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_spell( *this );
}

// ----------------------------------------------------------------------
// class for artifacts with a single spell effect
// ----------------------------------------------------------------------
bool t_single_spell::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	put<t_uint16>( buffer, m_spell );
	return true;
}

// ----------------------------------------------------------------------
// class for artifacts with a single spell effect
// ----------------------------------------------------------------------
int t_single_spell::get_level() const
{
	return get_spell_level( m_spell );
}

// ----------------------------------------------------------------------
// class for effects which affect a skill
// ----------------------------------------------------------------------
bool t_skill_effect::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;
	m_skill = t_skill_type( get<t_uint16>( buffer ));
	return true;
}

// ----------------------------------------------------------------------
// class for effects which affect a skill
// ----------------------------------------------------------------------
bool t_skill_effect::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_skill( *this );
}

// ----------------------------------------------------------------------
// class for effects which affect a skill
// ----------------------------------------------------------------------
bool t_skill_effect::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;
	put<t_uint16>( buffer, m_skill );
	return true;
}

// ----------------------------------------------------------------------
// class for spell charges
// ----------------------------------------------------------------------
int t_spell_charges_base::get_max_spell_points() const
{
	// make sure the max spell points is a multiple of the charge rate
	int result = get_spell_cost( m_spell ) * m_amount;
	int charge_rate = m_recharge_rate;
	int turns;

	if (charge_rate < 1)
		charge_rate = result;
	turns = ((result + charge_rate - 1) / charge_rate);
	return turns * charge_rate;
}

// ----------------------------------------------------------------------
// class for spell charges
// ----------------------------------------------------------------------
bool t_spell_charges_base::read( std::streambuf& buffer )
{
	if (!t_single_spell::read( buffer ))
		return false;
	m_spell_points = get<t_int16>( buffer );
	m_recharge_rate = get<t_uint8>( buffer );
	return true;
}

// ----------------------------------------------------------------------
// class for spell charges
// ----------------------------------------------------------------------
bool t_spell_charges_base::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_spell_charges( *this );
}

// ----------------------------------------------------------------------
// class for spell charges
// ----------------------------------------------------------------------
bool t_spell_charges_base::write( std::streambuf& buffer ) const
{
	if (!t_single_spell::write( buffer ))
		return false;
	put<t_int16>( buffer, m_spell_points );
	put<t_uint8>( buffer, m_recharge_rate );
	return true;
}

// ----------------------------------------------------------------------
// class for effects with multiple spell effects
// ----------------------------------------------------------------------
bool t_spell_list_effect::read( std::streambuf& buffer )
{
	if (!t_artifact_effect::read( buffer ))
		return false;

	int i;
	int size;

	size = get<t_uint16>( buffer );
	m_spells.clear();
	for ( i = 0; i < size; i++ )
		m_spells.insert( t_spell( get<t_uint16>( buffer ) ) );
	return true;
}

// ----------------------------------------------------------------------
// class for effects with multiple spell effects
// ----------------------------------------------------------------------
bool t_spell_list_effect::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_spell_list( *this );
}

// ----------------------------------------------------------------------
// class for effects with multiple spell effects
// ----------------------------------------------------------------------
bool t_spell_list_effect::write( std::streambuf& buffer ) const
{
	if (!t_artifact_effect::write( buffer ))
		return false;

	t_spell_set::const_iterator index;

	put<t_uint16>( buffer, m_spells.size() );
	for (index = m_spells.begin(); index != m_spells.end(); index++)
		put<t_uint16>( buffer, *index );
	return true;
}

// ----------------------------------------------------------------------
// class for effects which change spell costs
// ----------------------------------------------------------------------
bool t_spell_cost_base::read( std::streambuf& buffer )
{
	if (!t_spell_list_effect::read( buffer ))
		return false;
	m_percentage = get<t_int8>( buffer );
	return true;
}

// ----------------------------------------------------------------------
// class for effects which change spell costs
// ----------------------------------------------------------------------
bool t_spell_cost_base::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_spell_cost( *this );
}

// ----------------------------------------------------------------------
// class for effects which change spell costs
// ----------------------------------------------------------------------
bool t_spell_cost_base::write( std::streambuf& buffer ) const
{
	if (!t_spell_list_effect::write( buffer ))
		return false;
	put<t_int8>( buffer, m_percentage );
	return true;
}

// ----------------------------------------------------------------------
// class for effects which cast spells with attacks
// ----------------------------------------------------------------------
bool t_spell_with_attack_base::read( std::streambuf& buffer )
{
	if (!t_single_spell::read( buffer ))
		return false;
	return t_attack::read( buffer );
}

// ----------------------------------------------------------------------
// class for effects which cast spells with attacks
// ----------------------------------------------------------------------
bool t_spell_with_attack_base::accept( t_artifact_effect_visitor& visitor )
{
	return visitor.visit_spell_attack( *this );
}

// ----------------------------------------------------------------------
// class for effects which cast spells with attacks
// ----------------------------------------------------------------------
bool t_spell_with_attack_base::write( std::streambuf& buffer ) const
{
	if (!t_single_spell::write( buffer ))
		return false;
	return t_attack::write( buffer );
}

